<template>
  <div class="uploader-panel" v-if="internalShowUploader">
    <div class="uploader-title">
      <span>上传任务</span>
      <span class="close-icon" @click="closeUploader">×</span>
    </div>
    <!-- 添加updateTrigger引用，确保强制更新生效 -->
    <div :key="updateTrigger" class="file-list">
      <div v-for="(item, index) in fileList" :key="index" class="file-item">
        <div class="delete-btn" @click="deleteUploadTask(index)" title="删除上传任务">
          <img src="@/assets/icon-image/clean.png" alt="删除" />
        </div>
        <div class="file-info">
          <div class="file-name">{{ item.fileName }}</div>
        </div>
        <div class="file-status">
          <div class="status">
            <span :class="['status-' + item.status]">{{
              item.status == "success" ? "上传成功" : item.statusName
            }}</span>
          </div>
        </div>
        <div class="progress-bar-container">
          <div class="progress-bar">
            <div class="progress-fill" :class="['progress-' + item.status]" :style="{ width: item.progress + '%' }">
            </div>
          </div>
          <span class="progress-text">{{ item.progress }}%</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, defineProps, watch, nextTick, getCurrentInstance } from "vue";
import { uploadFile } from "@/api/file.js";
import SparkMD5 from "spark-md5";
import { ElMessage } from 'element-plus';

// 获取当前组件实例，用于强制更新DOM
// 获取当前组件实例
const instance = getCurrentInstance();

// 创建一个响应式的触发器，用于强制更新DOM
const updateTrigger = ref(0);

// 强制更新函数
const forceUpdate = () => {
  updateTrigger.value++;
  // 在Vue 3中，我们可以这样模拟强制更新
  if (instance && instance.proxy) {
    // 触发响应式更新
    updateTrigger.value++;
  }
};

// 接收父组件传递的showUploader属性
const props = defineProps({
  showUploader: {
    type: Boolean,
    default: false
  }
});

const internalShowUploader = ref(props.showUploader);
// 确保fileList是一个响应式数组
const fileList = ref([]);

// 定义emits
const emit = defineEmits(["uploadCallback", "update:showUploader"]);

// 监听父组件showUploader属性变化
watch(
  () => props.showUploader,
  (newVal) => {
    internalShowUploader.value = newVal;
  }
);

// 监听internalShowUploader变化，通知父组件
watch(
  internalShowUploader,
  (newVal) => {
    // 通知父组件状态变化
    emit('update:showUploader', newVal);
  }
);

// 添加文件到上传列表
const addFile = async (file) => {
  internalShowUploader.value = true;
  // 不再清除之前的文件，支持多次上传任务展示
  // fileList.value = [];
  // 创建一个新的文件项对象
  const fileItem = {
    file: file,
    fileName: file.name,
    //init, uploading, success, fail
    status: "uploading", // 直接设置为上传中
    statusName: "正在上传",
    md5: null,
    progress: 0 // 添加进度属性
  };
  // 将文件项添加到响应式数组中
  fileList.value.push(fileItem);

  // 强制触发DOM更新，确保文件项正确显示
  await nextTick();

  //开始上传
  await computeMD5(fileItem);
};

// 计算文件MD5值
const computeMD5 = (fileItem) => {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.readAsArrayBuffer(fileItem.file);
    fileReader.onload = (e) => {
      const buffer = e.target.result;
      const spark = new SparkMD5.ArrayBuffer();
      spark.append(buffer);
      fileItem.md5 = spark.end();
      upload(fileItem);
      resolve();
    };
    fileReader.onerror = (e) => {
      fileItem.status = "fail";
      fileItem.statusName = "MD5计算失败";
      console.error("MD5计算失败", e);
      ElMessage.error("MD5计算失败");
      reject(e);
    };
  });
};

// 上传文件
const upload = async (fileItem) => {
  console.log('开始上传文件:', fileItem.fileName);
  fileItem.status = "uploading";
  fileItem.statusName = "正在上传";
  fileItem.progress = 0; // 初始化进度
  console.log('上传状态已设置为:', fileItem.status);

  // 强制触发首次DOM更新，确保初始状态显示正确
  await nextTick();
  forceUpdate();

  const CHUNK_SIZE = Math.ceil(fileItem.file.size / 10); // 分为10块
  const totalChunks = 10;

  // 使用 Promise 包装上传过程，确保进度回调能被正确处理
  return new Promise(async (resolve, reject) => {
    try {
      for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
        const start = chunkIndex * CHUNK_SIZE;
        const end = Math.min(start + CHUNK_SIZE, fileItem.file.size);
        const chunk = fileItem.file.slice(start, end);

        let formData = new FormData();
        formData.append("file", chunk);
        formData.append("name", fileItem.fileName);
        formData.append("md5", fileItem.md5);
        formData.append("size", fileItem.file.size);
        formData.append("blockIndex", chunkIndex);
        formData.append("blockCount", totalChunks);

        // 更新当前块的上传状态
        fileItem.statusName = `准备上传 ${chunkIndex + 1}/${totalChunks}`;
        // 立即更新初始进度，避免卡在0%
        Object.assign(fileItem, { progress: Math.min(90, (chunkIndex + 1) * 10) });
        await nextTick();
        forceUpdate();

        try {
          // 更新状态为正在上传当前块
          fileItem.statusName = `正在上传 ${chunkIndex + 1}/${totalChunks}`;
          await nextTick();
          forceUpdate();

          const result = await uploadFile(formData);

          if (result && result.data) {
            const status = result.data.status;
            if (status === 2) {
              // 上传中，强制更新进度并触发响应式更新
              const newProgress = Math.min(90, (chunkIndex + 1) * 10);
              // 强制更新进度值并确保Vue检测到变化
              Object.assign(fileItem, { progress: newProgress });
              console.log(`上传进度更新为: ${fileItem.progress}%`);
              // 强制触发响应式更新
              await nextTick();
              forceUpdate();
            } else if (status === 1) {
              // 上传成功
              // 使用Object.assign确保Vue检测到变化
              Object.assign(fileItem, {
                progress: 100,
                status: "success",
                statusName: "上传成功"
              });
              await nextTick();
              forceUpdate();
              emit("uploadCallback");
              ElMessage.success(`文件"${fileItem.fileName}"上传成功！`);
              resolve(result);
              return; // 完全结束上传
            } else if (status === 0) {
              // 上传失败
              Object.assign(fileItem, {
                status: "fail",
                statusName: "上传失败"
              });
              await nextTick();
              forceUpdate();
              ElMessage.error(`文件"${fileItem.fileName}"上传失败`);
              emit("uploadCallback");
              reject(new Error('服务器返回上传失败'));
              return; // 完全结束上传
            }
          } else {
            // 处理其他情况，即使没有返回预期的状态，也更新进度
            console.log(`上传第${chunkIndex + 1}块返回结果:`, result);
            const newProgress = Math.min(90, (chunkIndex + 1) * 10);
            Object.assign(fileItem, { progress: newProgress });
            await nextTick();
            forceUpdate();
          }

          // 强制DOM更新
          await nextTick();
          forceUpdate();
        } catch (error) {
          console.error(`上传第${chunkIndex + 1}块出错:`, error);

          // 出错后直接标记为失败并停止上传
          Object.assign(fileItem, {
            status: "fail",
            statusName: `上传失败: 第${chunkIndex + 1}块上传失败`
          });
          await nextTick();
          forceUpdate();
          ElMessage.error(`文件"${fileItem.fileName}"上传失败，已停止上传`);
          emit("uploadCallback");
          reject(error);
          return; // 完全结束上传
        }
      }

      // 所有块都上传成功
      Object.assign(fileItem, {
        progress: 100,
        status: "success",
        statusName: "上传成功"
      });
      await nextTick();
      forceUpdate();
      emit("uploadCallback");
      ElMessage.success(`文件"${fileItem.fileName}"上传成功！`);
      resolve();
    } catch (error) {
      Object.assign(fileItem, {
        status: "fail",
        statusName: "上传失败: " + (error.message || '未知错误')
      });
      await nextTick();
      forceUpdate();
      console.error('上传错误:', error);
      ElMessage.error(`文件"${fileItem.fileName}"上传失败: ${error.message || '未知错误'}`);
      emit("uploadCallback");
      reject(error);
    }
  });
};

// 关闭上传面板
const closeUploader = () => {
  internalShowUploader.value = false;
};

// 切换上传面板显示状态（用于外部调用）
const toggleUploader = () => {
  internalShowUploader.value = !internalShowUploader.value;
};

// 删除上传任务
const deleteUploadTask = async (index) => {
  if (index >= 0 && index < fileList.value.length) {
    fileList.value.splice(index, 1);

    // 如果没有上传任务了，关闭上传面板
    if (fileList.value.length === 0) {
      internalShowUploader.value = false;
    }

    // 强制更新DOM
    await nextTick();
    forceUpdate();
  }
};

// 暴露方法给父组件
defineExpose({
  addFile,
  toggleUploader
});
</script>

<style lang="scss" scoped>
.uploader-panel {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 400px;
  max-height: 500px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 9999;
  overflow: hidden;

  .uploader-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 15px 20px;
    background-color: #f5f7fa;
    border-bottom: 1px solid #ebeef5;

    span:first-child {
      font-size: 16px;
      font-weight: bold;
      color: #303133;
    }

    .tips {
      font-size: 12px;
      color: #909399;
      margin-left: 10px;
    }

    .close-icon {
      font-size: 20px;
      color: #909399;
      cursor: pointer;
      padding: 0 5px;

      &:hover {
        color: #303133;
      }
    }
  }

  .file-list {
    padding: 10px;
    max-height: 400px;
    overflow-y: auto;

    .file-item {
      position: relative;
      margin-bottom: 10px;
      padding: 10px;
      background-color: #fafafa;
      border-radius: 4px;
      transition: background-color 0.3s;

      &:hover {
        background-color: #f0f0f0;
      }

      .delete-btn {
        position: absolute;
        top: 8px;
        right: 8px;
        width: 32px;
        height: 32px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 4px;
        transition: background-color 0.3s;

        &:hover {
          background-color: rgba(0, 0, 0, 0.1);
        }

        img {
          width: 30px;
          height: 30px;
          opacity: 0.6;
          transition: opacity 0.3s;
        }

        &:hover img {
          opacity: 1;
        }
      }

      .file-info {
        margin-bottom: 8px;

        .file-name {
          font-size: 14px;
          color: #303133;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
      }

      .file-status {
        margin-bottom: 8px;

        .status {
          .status-success {
            color: #67c23a;
          }

          .status-uploading {
            color: #409eff;
          }

          .status-fail {
            color: #f56c6c;
          }

          .status-waiting {
            color: #909399;
          }
        }
      }

      .progress-bar-container {
        display: flex;
        align-items: center;

        .progress-bar {
          flex: 1;
          height: 8px;
          background-color: #e4e7ed;
          border-radius: 4px;
          overflow: hidden;
          margin-right: 10px;

          .progress-fill {
            height: 100%;
            border-radius: 4px;
            transition: width 0.3s;
          }

          .progress-success {
            background-color: #67c23a;
          }

          .progress-uploading {
            background-color: #409eff;
          }

          .progress-fail {
            background-color: #f56c6c;
          }

          .progress-waiting {
            background-color: #c0c4cc;
          }
        }

        .progress-text {
          font-size: 12px;
          color: #606266;
          min-width: 40px;
          text-align: right;
        }
      }
    }
  }
}

/* 滚动条样式 */
.file-list::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

.file-list::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 3px;
}

.file-list::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 3px;
}

.file-list::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}
</style>